package com.common.base.utils

import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo
import android.app.Application
import android.app.DownloadManager
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.net.ConnectivityManager
import android.net.Uri
import android.net.wifi.WifiManager
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.telephony.TelephonyManager
import android.text.TextUtils
import android.util.Log
import android.view.Surface
import android.view.WindowManager
import androidx.annotation.*
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.common.base.app.BaseApplication
import com.common.base.app.extras.isNoEmpty
import com.common.base.app.extras.otherwise
import com.common.base.app.extras.yes
import com.common.base.config.AppConfig
import java.io.BufferedReader
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStreamReader
import java.lang.reflect.Field
import java.net.Inet4Address
import java.net.NetworkInterface
import java.net.SocketException
import java.nio.file.Files
import java.nio.file.Paths
import java.util.*


/**
 * App级别的工具类，提供系统的Context和常用的工具类
 */
object AppUtils {

    /**
     *  全局的Handler，发出了消息一定要自己手动关闭掉
     */
    private val mHandler: Handler = Handler(Looper.getMainLooper())

    /**
     * 获取全局的Application
     * @return Application
     */
    fun getApplication(): Application {
        return BaseApplication.getApplication()
    }

    /**
     * 获取全局的Context
     * @return Context
     */
    fun getContext(): Context {
        return getApplication().applicationContext
    }

    /**
     * 获取String资源集合
     */
    fun getString(@StringRes id: Int): String {
        return getContext().resources?.getString(id) ?: ""
    }

    /**
     * 获取String资源集合
     */
    fun getString(@StringRes id: Int, ext:String): String {
        return getContext().resources?.getString(id, ext) ?: ""
    }

    /**
     * 获取color资源
     */
    fun getColor(@ColorRes color: Int): Int {
        return ContextCompat.getColor(getContext(), color)
    }

    fun getColor(context: Context, @ColorRes color: Int): Int {
        return ContextCompat.getColor(context, color)
    }

    @JvmStatic
    fun getDimension(@DimenRes id: Int): Float {
        getContext().resources?.getDimension(id)?.let { return it }
        return 0F
    }

    @JvmStatic
    fun getDimensionPx(@DimenRes id: Int): Int {
        getContext().resources?.getDimensionPixelSize(id)?.let { return it }
        return 0
    }

    /**
     * 获取Drawable对象
     */
    @JvmStatic
    fun getDrawable(@DrawableRes id: Int): Drawable? {
        return if(id == 0) null else ContextCompat.getDrawable(getContext(), id)
    }

    /**
     * 获取String资源集合
     */
    fun getStringArray(@ArrayRes id: Int): Array<String> {
        return getContext().resources.getStringArray(id)
    }

    /**
     * 获取打包渠道
     * 使用该方法需要在manifest中配置CHANNEL的meta_data
     */
    fun getChannel(): String {
        var channel: String
        val appInfo: ApplicationInfo = getContext().packageManager.getApplicationInfo(
            getPackageName(),
            PackageManager.GET_META_DATA
        )
        if (appInfo.metaData.get("UMENG_CHANNEL") == null) {
            return "default"
        }
        channel = appInfo.metaData.get("UMENG_CHANNEL")!!.toString()
        //如果取到的渠道信息为空则传default
        if (IStringUtils.isEmpty(channel)) {
            channel = "default"
        }
        return channel
    }

    /**
     * Post一个Runnable
     */
    fun post(runnable: Runnable) {
        mHandler.post(runnable)
    }

    /**
     * 延时执行一个Runnable
     */
    fun postDelayed(runnable: Runnable, delayMillis: Long) {
        mHandler.postDelayed(runnable, delayMillis)

    }

    fun removeCallbacks(runnable: Runnable) {
        mHandler.removeCallbacks(runnable)
    }

    /**
     * 获取版本号
     */
    @Suppress("DEPRECATION")
    fun getVersionCode(): Int {
        return getPackageInfo().versionCode
    }

    /**
     * 获取版本名
     */
    fun getVersionName(): String {
        return getPackageInfo().versionName?:""
    }

    /**
     * 获取应用程序名称
     */
    fun getAppName(): String {
        val labelRes = getPackageInfo().applicationInfo?.labelRes
        return labelRes?.let { getContext().resources.getString(it) }?:""
    }


    /**
     * 获取当前应用的包名
     */
    fun getPackageName(): String {
        return getContext().packageName
    }


    /**
     * 获取包信息
     */
    fun getPackageInfo(): PackageInfo {
        return getContext().packageManager.getPackageInfo(getContext().packageName, 0)
    }

    /**
     * 手机设置厂商
     */
    fun getDeviceModel(): String {
        return Build.MODEL
    }

    fun getDeviceBrand(): String {
        return Build.BRAND
    }


    /**
     * @param slotId  slotId为卡槽Id，它的值为 0、1；
     * @return
     */
    fun getIMEI(slotId: Int): String {
        return try {
            val manager =
                getContext().getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
            val method = manager.javaClass.getMethod("getImei", Int::class.javaPrimitiveType!!)
            method.invoke(manager, slotId) as String
        } catch (e: Exception) {
            ""
        }
    }

    /**
     * 手机系统版本号，Android 6.0
     */
    fun getOsVersion(): String {
        return android.os.Build.VERSION.RELEASE
    }

    /**
     * 判断系统是否root
     */
    fun isSystemRoot(): Boolean {
        val binPath = "/system/bin/su"
        val xBinPath = "/system/xbin/su"

        if (File(binPath).exists() && isCanExecute(binPath)) {
            return true
        }
        return File(xBinPath).exists() && isCanExecute(xBinPath)
    }

    private fun isCanExecute(filePath: String): Boolean {
        var process: Process? = null
        try {
            process = Runtime.getRuntime().exec("ls -l $filePath")
            val bufferReader = BufferedReader(InputStreamReader(process.inputStream))
            val str: String = bufferReader.readLine()
            if (str.length >= 4) {
                var flag: Char = str[3]
                if (flag == 's' || flag == 'x')
                    return true
            }
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            process?.destroy()
        }
        return false
    }

    /**
     * 打开浏览器
     */
    fun startBrowser(url: String?) {
        try {
            if (!TextUtils.isEmpty(url)) {
                val uri = Uri.parse(url)
                val intent = Intent(Intent.ACTION_VIEW, uri)
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                getContext().startActivity(intent)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 打开设置, 设置权限
     */
    fun startSettingPermisssion(activity: Activity?) {
        val intent: Intent = Intent()
        try {
            intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS

            //8.0及以后版本使用这两个extra.  >=API 26
            intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
            intent.putExtra(Settings.EXTRA_CHANNEL_ID, getUid())

            //5.0-7.1 使用这两个extra.  <= API 25, >=API 21
            intent.putExtra("app_package", getPackageName())
            intent.putExtra("app_uid", getUid())

            activity?.startActivity(intent)
        } catch (e: Exception) {
            e.printStackTrace()

            //其他低版本或者异常情况，走该节点。进入APP设置界面
            intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
            intent.putExtra("package", getPackageName())

            activity?.startActivity(intent)
        }
    }

    /**
     * 打开设置, 设置权限
     */
    fun toLocationSettingPer(activity: Activity?) {
        try {
            val packageURI = Uri.parse("package:" + getPackageName())
            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI)
            activity?.startActivity(intent)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 判断当前APP是否在前台运行
     */
    fun isAppForeground(): Boolean {
        val activityManager = getContext().getSystemService(
            Context.ACTIVITY_SERVICE
        ) as ActivityManager
        val appProcesses = activityManager.runningAppProcesses ?: return false
        for (appProcess in appProcesses) {
            if (appProcess.processName == getPackageName() && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                return true
            }
        }
        return false
    }

    fun installApk(context: Context, downloadApkId: Long?) {
        val dManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
        downloadApkId?.let {
            val downloadFileUri = dManager.getUriForDownloadedFile(it)
            installApk(context, downloadFileUri)
        }
    }

    @SuppressLint("QueryPermissionsNeeded")
    fun installApk(context: Context, downloadFileUri: Uri?) {
        val install = Intent(Intent.ACTION_VIEW)
        if (downloadFileUri != null) {
            Log.d("DownloadManager", downloadFileUri.toString())
            install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive")
            if ((Build.VERSION.SDK_INT >= 24)) {//判读版本是否在7.0以上
                install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) //添加这一句表示对目标应用临时授权该Uri所代表的文件
            }
            install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            if (install.resolveActivity(context.packageManager) != null) {
                context.startActivity(install)
            } else {
                Log.e("installApk", "自动安装失败，请手动安装")
            }
        } else {
            Log.e("DownloadManager", "download error")
        }
    }

    /**
     * 获取当前应用的信息
     */
    fun getAppInfo(): AppInfo? = getAppInfo(getContext().packageName)


    /**
     * 获取指定包名的应用信息
     * @param packageName 包名
     */
    fun getAppInfo(packageName: String): AppInfo? {
        return try {
            val pm = getContext().packageManager
            val pi = pm.getPackageInfo(packageName, 0)
            getAppInfo(pm, pi)
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
            null
        }

    }

    fun getAppsInfo(): List<AppInfo> {
        val list = ArrayList<AppInfo>()
        val pm = getContext().packageManager
        val installedPackages = pm.getInstalledPackages(0)
        for (pi in installedPackages) {
            val ai = getAppInfo(pm, pi) ?: continue
            list.add(ai)
        }
        return list
    }

    /**
     * 已知包名来判断APP是否安装
     * @param packageName 包名
     */
    fun isInstalled(packageName: String?): Boolean {
        var curInstalled = false

        packageName?.let {
            curInstalled = try {
                getContext().packageManager.getApplicationInfo(packageName, 0)
                true
            } catch (var3: PackageManager.NameNotFoundException) {
                try {
                    getContext().packageManager.getApplicationInfo(packageName, 0)
                    true
                } catch (var2: PackageManager.NameNotFoundException) {
                    false
                }
            }
        }
        return curInstalled
    }

    @Suppress("DEPRECATION")
    private fun getAppInfo(pm: PackageManager?, pi: PackageInfo?): AppInfo? {
        if (pm == null || pi == null) return null
        val ai = pi.applicationInfo
        val packageName = pi.packageName
        val name = ai?.loadLabel(pm).toString()
        val icon = ai?.loadIcon(pm)
        val packagePath = ai?.sourceDir
        val versionName = pi.versionName
        val versionCode = pi.versionCode
        val isSystem = ApplicationInfo.FLAG_SYSTEM and (ai?.flags?:0) != 0
        return AppInfo(packageName, name, icon, packagePath, versionName, versionCode, isSystem)
    }

    private fun isFileExists(file: File?): Boolean {
        return file != null && file.exists()
    }

    private fun getFileByPath(filePath: String): File? {
        return if (isSpace(filePath)) null else File(filePath)
    }

    private fun isSpace(s: String?): Boolean {
        if (s == null) return true
        var i = 0
        val len = s.length
        while (i < len) {
            if (!Character.isWhitespace(s[i])) {
                return false
            }
            ++i
        }
        return true
    }

    private fun isDeviceRooted(): Boolean {
        val su = "su"
        val locations = arrayOf(
            "/system/bin/",
            "/system/xbin/",
            "/sbin/",
            "/system/sd/xbin/",
            "/system/bin/failsafe/",
            "/data/local/xbin/",
            "/data/local/bin/",
            "/data/local/"
        )
        for (location in locations) {
            if (File(location + su).exists()) {
                return true
            }
        }
        return false
    }

    /**
     * The application's information.
     */
    class AppInfo(
        packageName: String, name: String, icon: Drawable?, packagePath: String?,
        versionName: String?, versionCode: Int, isSystem: Boolean
    ) {

        var packageName: String? = null
        var name: String? = null
        var icon: Drawable? = null
        var packagePath: String? = null
        var versionName: String? = null
        var versionCode: Int = 0
        var isSystem: Boolean = false

        init {
            this.name = name
            this.icon = icon
            this.packageName = packageName
            this.packagePath = packagePath
            this.versionName = versionName
            this.versionCode = versionCode
            this.isSystem = isSystem
        }

        override fun toString(): String {
            return "pkg name: " + packageName +
                    "\napp icon: " + icon +
                    "\napp name: " + name +
                    "\napp path: " + packagePath +
                    "\napp v name: " + versionName +
                    "\napp v code: " + versionCode +
                    "\nis system: " + isSystem
        }
    }

    fun getUid(): String {
        var uid = ""
        try {
            val pm = getContext().packageManager
            val ai = pm.getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA)
            uid = ai.uid.toString()
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        }

        return uid
    }

    fun toString(value: Any?, def: String = ""): String {
        val vStr = value?.toString()
        if (vStr.isNoEmpty()) {
            return vStr ?: def
        }
        return def
    }

    fun toBoolean(value: Any?): Boolean {
        val vStr = value?.toString()
        if (vStr.isNoEmpty()) {
            return vStr?.toBoolean() ?: false
        }
        return false
    }

    fun toInt(value: Any?, def: Int = 0): Int {
        try {
            val vStr = value?.toString()
            if (vStr.isNoEmpty()) {
                return vStr?.toDouble()?.toInt() ?: def
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return def
    }

    fun toFloat(value: Any?): Float {
        try {
            val vStr = value?.toString()
            if (vStr.isNoEmpty()) {
                return vStr?.toDouble()?.toFloat() ?: 0F
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return 0F
    }

    fun toLong(value: Any?): Long {
        try {
            val vStr = value?.toString()
            if (vStr.isNoEmpty()) {
                return vStr?.toDouble()?.toLong() ?: 0L
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return 0L
    }

    fun toDouble(value: Any?): Double {
        try {
            val vStr = value?.toString()
            if (vStr.isNoEmpty()) {
                return vStr?.toDouble() ?: 0.0
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return 0.0
    }

    fun isHorizontalScreen(): Boolean {
        val angle =
            (getApplication().getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay.rotation;
        Log.e("wall", "手机屏幕的 angle 是多少 $angle")
        if (angle == Surface.ROTATION_90 || angle == Surface.ROTATION_270) {
            //如果屏幕旋转90°或者270°是判断为横屏，横屏规避不展示
            return true
        }
        return false
    }

    fun commitFragment(fragmentManager: FragmentManager, fragment: Fragment?, layout: Int) {
        val fragmentTransaction = fragmentManager.beginTransaction()
        fragment?.let {
            fragmentTransaction.add(layout, it)
            fragmentTransaction.commit()
        }
    }

    fun replaceFragment(fragmentManager: FragmentManager, fragment: Fragment?, layout: Int) {
        val transaction = fragmentManager.beginTransaction()
        fragment?.let {
            transaction.replace(layout, it)
            transaction.commit()
        }
    }

    //是否需求强制更新APP
    fun isNeedUpdateApp(context: Context): Boolean {
        val updateVersionNo = SpUtilsManagement.getString(AppConfig.KEY_APK_DOWNLOAD_IS_COERCE)
        return (updateVersionNo != VersionUtils.getVerName(context))
    }

    @SuppressLint("WifiManagerLeak")
    fun getIpAddress(): String? {
        var ip = ""
        try {
            val info = (getContext()
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).activeNetworkInfo
            if (info != null && info.isConnected) {
                if (info.getType() == ConnectivityManager.TYPE_MOBILE) {//当前使用2G/3G/4G网络
                    val en = NetworkInterface.getNetworkInterfaces()
                    while (en.hasMoreElements()) {
                        val intf = en.nextElement()
                        val enumIpAddr = intf.inetAddresses
                        while (enumIpAddr.hasMoreElements()) {
                            val inetAddress = enumIpAddr.nextElement()
                            if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) {
                                return inetAddress.getHostAddress()
                            }
                        }
                    }
                } else if (info.getType() == ConnectivityManager.TYPE_WIFI) {//当前使用无线网络
                    val wifiManager =
                        getContext().getSystemService(Context.WIFI_SERVICE) as WifiManager
                    val wifiInfo = wifiManager.connectionInfo
                    ip = intToIp(wifiInfo.ipAddress) //得到IPV4地址
                }
            }
        } catch (e: SocketException) {
            e.printStackTrace()
        }
        return ip
    }

    private fun intToIp(i: Int): String {
        return "${i and 0xFF}.${i shr 8 and 0xFF}.${i shr 16 and 0xFF}.${i shr 24 and 0xFF}"
    }

    fun exitApp() {
        //释放内存，退出程序
        android.os.Process.killProcess(android.os.Process.myPid())
    }

    /**
     * 判断是否已经有安装某个app
     * @param packageName 包名
     */
    fun isAppInstalled(packageName: String): Boolean {
        val list = getContext().packageManager.getInstalledPackages(0)
        list.forEach {
            if (it.packageName == packageName) {
                return true
            }
        }
        return false
    }

    //判断是否有悬浮窗的权限
    fun hasDrawOverlaysPermission(context: Context?): Boolean {
        return if (Build.VERSION.SDK_INT >= 23) {
            Settings.canDrawOverlays(context)
        } else {
            false
        }
    }

    fun getSource(): String {
        return "1;${VersionUtils.getVerName(getContext())};${getChannel()}"
    }

    fun patchCheck(): Boolean {
        val filesDir = getContext().filesDir.absolutePath
        val packageName = getContext().applicationContext.packageName
        val normalPathOne = "/data/data/$packageName/files"
        val normalPathTwo = "/data/user/0/$packageName/files"
        return (normalPathOne == filesDir) || (normalPathTwo == filesDir)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    fun isDualAppEx(dataDir: String): Boolean {
        try {
            val simpleName = "wtf_jack"
            val testPath = dataDir + File.separator + simpleName
            val fos = FileOutputStream(testPath)
            val fileDescriptor = fos.fd
            val fid_descriptor: Field = fileDescriptor.javaClass.getDeclaredField("descriptor")
            fid_descriptor.isAccessible = true
            // 获取到 fd
            val fd = fid_descriptor.get(fileDescriptor) as Int
            // fd 反查真实路径
            val fdPath = String.format("/proc/self/fd/%d", fd)
            val realPath: String = Files.readSymbolicLink(Paths.get(fdPath)).toString()
            if (realPath.substring(realPath.lastIndexOf(File.separator)) != File.separator + simpleName) {
                // 文件名被修改
                return true
            }
            // 尝试访问真实路径的父目录
            val fatherDirPath = realPath.replace(simpleName, "..")
            Log.d("TAG", "isDualAppEx: $fatherDirPath")
            val fatherDir = File(fatherDirPath)
            if (fatherDir.canRead()) {
                // 父目录可访问
                return true
            }
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
            return true
        }
        return false
    }
    fun isDualApp(dataDir: String): Boolean {
        return File(dataDir + File.separator + "..").canRead()
    }

    /**
     * 判断是否主进程
     */
    @JvmStatic
    fun isMainProcess(context: Context): Boolean {
        val pid = android.os.Process.myPid()
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        for (appProcess in activityManager.runningAppProcesses) {
            if (appProcess.processName == context.packageName) {
                return appProcess.pid == pid
            }
        }
        return false
    }
}